home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Disc to the Future 2
/
Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin
/
MAC
/
THINKC
/
4_0
/
VANTAGE_
/
DELETE_F.C
< prev
next >
Wrap
C/C++ Source or Header
|
1989-09-05
|
23KB
|
969 lines
/**************************************************************/
/* This is a Vantage VCMD written in LightSpeed C V3.0.
It allows a group of files in a folder to be deleted with one
command. The files to be deleted are specified by an MS-DOS
style wildcard filename and may also be filtered by file type
or creator.
Feel free to use it, abuse it, or distribute it as you see fit.
To build the VCMD resource, set the following project types:
Code Resource
File Type = VEXT
Creator = VEXI
Custom Header = ON (checked)
Type = VCMD
ID = 1 (arbitrary)
Attrs = 20
Also include the MacTraps and strings libraries and check the
MacHeaders option in the code generation options dialog.
Dave McWherter, 7/23/89
Signature Software
215-639-8764
*/
/**************************************************************/
#include <MacTypes.h>
/**************************************************************/
/* Data type aliases: */
#ifndef bool
#define bool int
#endif
#ifndef uchar
#define uchar unsigned char
#endif
/****************************************************************/
/* Status codes for Vantage: */
#define OK 0
#define PASTE_OK 1
#define ERR -1
/****************************************************************/
/* Vantage's SFGet dialog and its buttons (defined in Vantage): */
#define SFGetDlog 5
#define OpenButton 1
#define CancelButton 3
#define ListFilesButton 11
/****************************************************************/
/* Items in our setup dialog: */
#define OKBUTTON 1
#define CANCELBUTTON 2
#define NAMETEXT 5
#define TYPETEXT 9
#define CREATORTEXT 11
/****************************************************************/
/* Misc. stuff: */
#define MAXLIST 5000 /* maximum size of our list of filenames or result text */
/****************************************************************/
/* Prototypes for our routines: */
void header(void);
void main(void);
pascal int open(uchar *vantdata, char **errmsg);
pascal int close(uchar *vantdata, char **errmsg);
pascal int execute(uchar *oldtext, long oldlength, Handle *newtext, long *newlength,
uchar *vantdata, Handle winddata, char **errmsg);
pascal int idle(Handle menu, int mitem, uchar *vantdata, Handle winddata, char **errmsg);
void return_errmsg(char *msg, char **msgptr);
void putbyte(uchar byt);
void putstring(char *str);
pascal int SFDlogHook(int item, DialogPtr dlogptr);
pascal bool SFFileFilter(HFileInfo *iop);
bool do_param_dialog(void);
long Get4TextChars(DialogPtr dlogptr, int itemnum);
void Set4TextChars(DialogPtr dlogptr, long itemval, int itemnum);
bool match_file(HFileInfo *ioparms);
bool match_name(char *name);
char tolower(char c);
char *strcpy(char *s1, char *s2);
/****************************************************************/
/* Our custom code header used by Vantage to call the VCMD.
Several things to note here:
1.) Be very careful of the alignment forced by the dc.b statement.
LSC aligns each dc.b. I don't know of any other way in LSC to
generate these Pascal strings needed in the Vantage header, which
means every time you change the strings, you have to hand-count
the bytes, change the size bytes, adjust the pad bytes, and worry
about alignment.
2.) LSC can only generate PC-relative offsets in the dc.w statement.
Since Vantage wants header-relative offsets for each of the entry
points, we have to correct the offsets generated by the LSC assembler
when we Open. Fortunately, the Open offset is the same in either
method of calculation and Open is always called first, so we can fix
the other three offsets when we Open
3.) Since we're using a custom header, we have to load A0 ourselves
on entry to each routine. (Actually, we only need A0 setup so that
RememberA0() works, and we only need RememberA0 if we need globals.
Only our Execute routine needs globals (implicitly for the error
strings), so we really only need the A0 setup and SetUpA4() and
RestoreA4() stuff in the Execute routine. But I put it all in
every routine just for consistency's sake.
*/
void header()
{
asm {
dc.w @1 ; Offset to Open routine
dc.w @2 ; Offset to Close routine (almost - see note)
dc.w @3 ; Offset to Execute routine (almost - see note)
dc.w @4 ; Offset to Idle routine (almost - see note)
dc.w 0x0100 ; First byte is the need_idle_calls boolean (we do)
; Second byte is spare reserved for future use
; Next 50 bytes are the menu title (and pad) for this VCMD...
dc.b 15, 'D', 'e', 'l', 'e', 't', 'e', ' ', 'F', 'i', 'l', 'e', 's', '.', '.', '.'
dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
dc.b 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
dc.b 0, 0, 0, 0
; The VCMD installer identification string...
dc.b 13, 'F', 'i', 'l', 'e', 's', ' ', 'D', 'e', 'l', 'e', 't', 'e', 'r'
@1: lea header,a0 ; We must load A0 ourselves so RememberA0() works
addq.w #2,2(a0) ; Fix up other three offsets per above note
addq.w #4,4(a0)
addq.w #6,6(a0)
jmp open
@2: lea header,a0
jmp close
@3: lea header,a0
jmp execute
@4: lea header,a0
jmp idle
}
}
/**************************************************************/
/* Include LSC's magical A0 and A4 stuff... */
/* This must be somewhere _after_ the header() function. */
static __GetA4(void);
void SetUpA4(void);
#include <SetupA4.h>
/****************************************************************/
void main()
{
/* We don't use a main() */
}
/****************************************************************/
/* Globals: */
int sfgresult; /* result from SFGet dialog (1 = cancel, 2 = do it) */
HFileInfo ioparms; /* parameter block for I/O calls */
Handle flisthdl; /* list of files to open */
char *flist; /* ptr to same */
Handle reshdl; /* result text we send back to Vantage */
long ressize; /* size of same */
char *put; /* ptr to next free char in current list (file list or result text) */
long putsize; /* size of current list */
bool toobig; /* TRUE if too many files */
int itype; /* used to GetDItem's */
Handle ihdl;
Rect irect;
Str255 dfilename; /* the wildcard filename specified in the setup dialog */
long dtype; /* the file type in same */
long dcreator; /* the file creator in same */
extern char ParmDITLList[], ParmDlogRect[]; /* the setup dialog's DITL and bounds rect
(in the .asm file) */
bool error; /* TRUE if an error has occurred */
bool needupdate; /* TRUE if we need to force a Vantage update in our idle routine */
/****************************************************************/
/* Called once when Vantage is opened. Use it to do any necessary
setup. If you return an error code, then your VCMD resource is
dumped and won't be called again. */
pascal int open(vantdata, errmsg)
uchar *vantdata;
char **errmsg;
{
int result;
RememberA0();
SetUpA4();
dfilename[0] = 0; /* initialize our dialog parameter fields */
dtype = dcreator = 0;
needupdate = FALSE; /* don't have to force an update yet */
RestoreA4();
return OK; /* Tell Vantage the result. */
}
/****************************************************************/
/* Called when Vantage is closed. After you're closed, Vantage dumps
the VCMD resource. */
pascal int close(vantdata, errmsg)
uchar *vantdata;
char **errmsg;
{
/* We don't need any close processing
RememberA0();
SetUpA4();
do_close_stuff();
RestoreA4();
*/
return OK;
}
/****************************************************************/
/* The Execute routine is the body of the VCMD. It's called when the
user selects your VCMD menu item from Vantage's External menu. You
can do whatever you want when Execute is called, but Vantage assumes
you're going to operate on the text in the current window, so he
passes you that text and allows you to return new text to replace it.
If you don't operate on the text, then you must tell Vantage that by
returning 'newtext' set to zero. If you do return any replacement text,
then Vantage will dispose of the newtext handle you return after it
pastes in the newtext. (But if you return an error code, then Vantage
assumes that you've disposed of any memory that you allocated.)
The text you're passed is all of the text in the window if nothing is
selected, otherwise only the selected text is passed. If you return a
result code of 0, then Vantage replaces all of the passed text with the
new text you return. If you return a result code of 1, then Vantage will
use the new text for a standard Paste operation.
There are also two other special result codes:
result = 2 = set selection in current window to newtext (start)
and newlength (end)
result = 3 = set selection in current window to newtext (start)
and newlength (end) and then scroll to that selection
(Result codes 2 and 3 are only supported in Vantage V2.0 or above.)
*/
pascal int execute(oldtext, oldlength, newtext, newlength, vantdata, winddata, errmsg)
uchar *oldtext;
long oldlength;
Handle *newtext;
long *newlength;
uchar *vantdata;
Handle winddata;
char **errmsg;
{
unsigned int resourcebase, saverescfile;
int i, index, numfiles;
uchar *p;
SFReply reply;
Str255 filename;
long dirid;
RememberA0();
SetUpA4();
/* get the base value of Vantage's resources. */
asm {
move.l vantdata,a0
move.w 0x26(a0),resourcebase
}
error = FALSE;
*newtext = *newlength = 0;
flisthdl = reshdl = toobig = numfiles = ressize = 0;
/* save the current resource file path and make sure we go to the system
file for Vantage's SFGet dialog resource. */
saverescfile = CurResFile();
UseResFile(0);
InitCursor();
if (FSFCBLen == -1) {
return_errmsg("\pThis VCMD only works with HFS!", errmsg);
}
else {
flisthdl = NewHandle(MAXLIST); /* get a handle for the list of filenames in the folder */
if (flisthdl == 0) {
return_errmsg("\pOut of memory in Delete Files VCMD!", errmsg);
}
else {
reshdl = NewHandle(MAXLIST); /* get a handle for the result text we'll return to Vantage */
if (reshdl == 0) {
return_errmsg("\pOut of memory in Delete Files VCMD!", errmsg);
}
else {
if (do_param_dialog()) { /* get delete parameters from user */
HLock(flisthdl); /* prepare to collect matching filenames into our list */
put = *flisthdl; putsize = 0;
/* let them pick a folder using Vantage's custom SFGet dialog */
SFPGetFile(0x400040, 0, &SFFileFilter, -1, 0, &SFDlogHook, &reply, (SFGetDlog+resourcebase), 0);
if (sfgresult == 1) {
/* if they didn't cancel it... */
p = (uchar *) &ioparms; /* clear the I/O parameter block */
for (i = 0; i < sizeof(HFileInfo); i++)
*p++ = 0;
ioparms.ioVRefNum = reply.vRefNum; /* which volume */
dirid = CurDirStore; /* which directory */
index = 1; /* start with first file in folder */
/* collect all the files which match the filters into flist */
while (TRUE) {
ioparms.ioFDirIndex = index++;
ioparms.ioDirID = dirid;
ioparms.ioNamePtr = (uchar *) &filename;
if (PBGetCatInfo(&ioparms, FALSE) != 0)
break; /* no more files in folder */
if (!(ioparms.ioFlAttrib & 0x10)) { /* ignore folder names */
if (match_file(&ioparms)) { /* does file match filters? */
putbyte(filename[0]); /* yes - remember its name */
putstring((char *) &filename);
numfiles++;
}
}
if (toobig)
break; /* too many filenames! */
}
flist = *flisthdl; /* reset ptr back to filename list beginning */
HLock(reshdl); /* prepare to stuff result text into reshdl */
put = *reshdl;
putsize = 0;
if (numfiles) {
/* build a text message detailing the files we delete or not... */
putstring("\p\rDeleting following files...\r");
while (numfiles--) {
putbyte(0x09); /* tab and then filename */
putstring(flist);
ioparms.ioFDirIndex = 0;
ioparms.ioDirID = dirid;
ioparms.ioNamePtr = (uchar *) flist;
if (PBDelete(&ioparms, FALSE) == 0) {
putstring("\p\tOK");
}
else {
putstring("\p\t***--> Error - couldn't delete.");
}
putbyte(0x0d); /* one file per line */
flist += (*flist + 1); /* bump to next file name in list */
if (toobig)
break; /* too many filenames! */
}
ressize = putsize;
}
else {
return_errmsg("\pNo files match the filters!", errmsg);
}
}
}
}
}
}
if (toobig)
return_errmsg("\pToo many files - aborted!", errmsg);
UseResFile(saverescfile);
if (ressize) { /* if we have result text */
*newtext = reshdl; /* then give it to Vantage */
*newlength = putsize;
HUnlock(reshdl);
}
else {
if (reshdl)
DisposHandle(reshdl); /* else dump the unused result text handle */
}
if (flisthdl) /* we're done with our filename list now */
DisposHandle(flisthdl);
needupdate = TRUE; /* see comment in idle routine */
if (error) {
RestoreA4();
return ERR;
}
else {
RestoreA4();
return PASTE_OK; /* don't replace text - do a normal paste */
}
}
/****************************************************************/
/* Given a file from the folder, decide if we want to delete it.
File has to match our wildcard name, our type, and our creator. */
bool match_file(iop)
HFileInfo *iop;
{
if (dfilename[0]) {
if (!match_name((char *) iop->ioNamePtr))
return FALSE;
}
if (dtype) {
if (iop->ioFlFndrInfo.fdType != dtype)
return FALSE;
}
if (dcreator) {
if (iop->ioFlFndrInfo.fdCreator != dcreator)
return FALSE;
}
return TRUE;
}
/****************************************************************/
/* Given a filename, determine if it matches our wildcard name.
'*' matches any group of characters.
'?' matches any single character.
(Just like MS-DOS or CP/M, pardon my language.)
/* Kludge alert!!! */
bool match_name(name)
char *name;
{
char pattern[256], query[256];
char *p, *q, c;
if (!dfilename[0])
return FALSE;
PtoCstr((char *) dfilename);
PtoCstr(name);
strcpy(pattern, (char *) dfilename);
strcpy(query, name);
CtoPstr((char *) dfilename);
CtoPstr(name);
for (p = pattern; *p; p++)
*p = tolower(*p);
for (q = query; *q; q++)
*q = tolower(*q);
p = pattern;
q = query;
while (c = *p++) {
if (c == '*') {
if (!(c = *p++))
return TRUE;
while (*q) {
if (*q++ == c)
break;
}
if (!*q) {
if ((!*p) && (q[-1] == c))
return TRUE;
else
return FALSE;
}
}
else if (c == '?') {
q++;
}
else {
if (c != *q++)
return FALSE;
}
if ((!*p) && (!*q))
return TRUE;
if (!*p)
return FALSE;
if (!*q) {
if ((*p == '*') && (!p[1]))
return TRUE;
else
return FALSE;
}
}
if (!*q)
return TRUE;
else
return FALSE;
}
/****************************************************************/
/* Get the wildcard filename and the allowable file types and creators
from the user. */
bool do_param_dialog()
{
int ditlsize, i, hit;
char *p;
GrafPort *saveport;
DialogPtr dlogptr;
Handle ditlhdl;
/* First stuff our DITL list into a handle. */
ditlsize = (ParmDlogRect - ParmDITLList);
ditlhdl = NewHandle(ditlsize);
if (ditlhdl == 0)
return FALSE;
p = *ditlhdl;
i = 0;
while (ditlsize--)
*p++ = ParmDITLList[i++];
/* save the current port, display the dialog, and set it as the current port */
GetPort(&saveport);
dlogptr = NewDialog(0, &ParmDlogRect, "", -1, 1, -1, 0, 0, ditlhdl);
SetPort(dlogptr);
/* default the parameters to whatever they were on last use of the VCMD */
GetDItem(dlogptr, NAMETEXT, &itype, &ihdl, &irect);
SetIText(ihdl, &dfilename);
SelIText(dlogptr, NAMETEXT, 0, 32000);
Set4TextChars(dlogptr, dtype, TYPETEXT);
Set4TextChars(dlogptr, dcreator, CREATORTEXT);
/* highlight the OK button */
GetDItem(dlogptr, OKBUTTON, &itype, &ihdl, &irect);
PenSize(3, 3);
InsetRect(&irect, -4, -4);
FrameRoundRect(&irect, 16, 16);
PenNormal();
/* handle events in the dialog */
while (TRUE) {
ModalDialog(0, &hit);
switch (hit) {
case OKBUTTON:
GetDItem(dlogptr, NAMETEXT, &itype, &ihdl, &irect);
GetIText(ihdl, &dfilename);
dtype = Get4TextChars(dlogptr, TYPETEXT);
dcreator = Get4TextChars(dlogptr, CREATORTEXT);
DisposDialog(dlogptr);
SetPort(saveport);
/* make sure they specify at least one parameter as a safety net */
if (dfilename[0] | dtype | dcreator)
return TRUE;
else
return FALSE;
case CANCELBUTTON:
DisposDialog(dlogptr);
SetPort(saveport);
return FALSE;
case NAMETEXT:
case TYPETEXT:
case CREATORTEXT:
default:
break;
}
}
}
/****************************************************************/
/* Used to set the file type or creator in the dialog. */
void Set4TextChars(dlogptr, itemval, itemnum)
DialogPtr dlogptr;
long itemval;
int itemnum;
{
Str255 text;
if (!itemval)
text[0] = 0;
else {
text[0] = 4;
text[1] = (itemval & 0xFF000000L) >> 24;
text[2] = (itemval & 0x00FF0000L) >> 16;
text[3] = (itemval & 0x0000FF00L) >> 8;
text[4] = (itemval & 0x000000FFL);
}
GetDItem(dlogptr, itemnum, &itype, &ihdl, &irect);
SetIText(ihdl, &text);
}
/****************************************************************/
/* Used to get the file type or creator from the dialog. */
long Get4TextChars(dlogptr, itemnum)
DialogPtr dlogptr;
int itemnum;
{
int i;
Str255 text;
long result;
GetDItem(dlogptr, itemnum, &itype, &ihdl, &irect);
GetIText(ihdl, &text);
if (text[0] < 4)
return 0;
else {
result = 0;
for (i = 1; i <= 4; i++)
result = (result << 8) | text[i];
return result;
}
}
/****************************************************************/
/* Our filter proc for the SFGet dialog where we decide if we want
to display a filename in the filename list. */
pascal bool SFFileFilter(iop)
HFileInfo *iop;
{
bool res;
SetUpA4();
res = match_file(iop);
RestoreA4();
if (res)
return FALSE;
else
return -1;
}
/****************************************************************/
/* Handles the non-standard buttons in Vantage's SFGet dialog. */
pascal int SFDlogHook(item, dlogptr)
int item;
DialogPtr dlogptr;
{
int result;
SetUpA4();
result = item;
if (item == -1) {
GetDItem(dlogptr, ListFilesButton, &itype, &ihdl, &irect);
SetCTitle(ihdl, "\pDelete Files");
}
else if ((item == ListFilesButton) || (item == OpenButton)) {
result = OpenButton;
sfgresult = 1;
}
else if (item == CancelButton) {
sfgresult = 2;
}
RestoreA4();
return result;
}
/****************************************************************/
/* Stuff a byte into our current list. */
void putbyte(byt)
uchar byt;
{
if (putsize >= MAXLIST) {
toobig = TRUE;
}
else {
*put++ = byt;
putsize++;
}
}
/****************************************************************/
/* Stuff a string into our current list. */
void putstring(str)
char *str;
{
int length;
length = *str++;
while (length) {
putbyte(*str++);
length--;
}
}
/****************************************************************/
char tolower(c)
char c;
{
if ((c >= 'A') && (c <= 'Z'))
c |= 0x20;
return c;
}
/****************************************************************/
/* The Idle routine gives you a chance to periodically perform tasks.
You don't need an Idle routine unless you tell Vantage you want
idle calls in the boolean byte in the header. */
pascal int idle(menu, mitem, vantdata, winddata, errmsg)
Handle menu;
int mitem;
uchar *vantdata;
Handle winddata;
char **errmsg;
{
uchar *p;
EventRecord event;
RememberA0();
SetUpA4();
/* We need an idle routine just for cosmetic reasons. Since we had two dialogs
up without going back to Vantage, all of the current Vantage window doesn't
always get updated after we're done. I'm not sure if this is a Vantage problem
or a system problem, but I suspect it's something in the system. At any rate,
if we force a complete update of Vantage's current window here the problem
is fixed. */
if (needupdate) { /* if we've just done a delete */
if (!EventAvail(updateMask, &event)) { /* let all normal updates proceed first */
asm { /* then force an update by invalidating Vantage's window */
move.l winddata,a0
move.l (a0),a0 /* a0 = ptr to Vantage's current window's record */
move.l (a0),a0 /* a0 = window ptr for same */
lea 16(a0),a0 /* a0 = address of window's portRect */
move.l a0,p
}
InvalRect(p); /* invalidate window's portRect */
needupdate = FALSE; /* only do this once after a delete */
}
}
RestoreA4();
return OK;
}
/****************************************************************/
/* Returns a pointer to an error message to Vantage. */
void return_errmsg(msg, msgptr)
char *msg;
char **msgptr;
{
*msgptr = msg;
error = TRUE;
}
/****************************************************************/
/* Here's the assembly source file I used to create the DITL list
for the parameters dialog. (This is in McAssembly format.) You
can make the DITL list anyway you want to. I just found it
easier to do it in assembly then in C.
BUTTONITEM equ 4
RADIOBUTTONITEM equ 6
STATICTEXTITEM equ 8
EDITTEXTITEM equ 16
def ParmDITLList, ParmDlogRect
ParmDITLList
dc.w 12-1
.1 dc.l 0
dc.w 210, 188, 230, 248
dc.b BUTTONITEM
text #"OK"
align
.2 dc.l 0
dc.w 210, 56, 230, 116
dc.b BUTTONITEM
text #"Cancel"
align
.3 dc.l 0
dc.w 4, 77, 25, 220
dc.b STATICTEXTITEM
text #"Delete file filters..."
align
.4 dc.l 0
dc.w 105, 30, 125, 76
dc.b STATICTEXTITEM
text #"Name:"
align
.5 dc.l 0
dc.w 105, 84, 122, 289
dc.b EDITTEXTITEM
text #""
align
.6 dc.l 0
dc.w 130, 50, 150, 295
dc.b STATICTEXTITEM
text #"'*' matches any group of characters."
align
.7 dc.l 0
dc.w 155, 50, 175, 279
dc.b STATICTEXTITEM
text #"'?' matches any single character."
align
.8 dc.l 0
dc.w 180, 30, 200, 72
dc.b STATICTEXTITEM
text #"Type:"
align
.9 dc.l 0
dc.w 180, 82, 197, 127
dc.b EDITTEXTITEM
text #""
align
.10 dc.l 0
dc.w 180, 166, 200, 226
dc.b STATICTEXTITEM
text #"Creator:"
align
.11 dc.l 0
dc.w 180, 235, 197, 280
dc.b EDITTEXTITEM
text #""
align
.12 dc.l 0
dc.w 29, 9, 97, 307
dc.b STATICTEXTITEM
dc.b .12end - .12beg
.12beg text "Only files matching all of the specified filters will be deleted. "
text "Blank filters are not used in the filtering process, but if all filters "
text "are blank then no files will be deleted."
.12end
align
ParmDlogRect
dc.w 64, 64, 304, 374
/****************************************************************/